/**
 * Creep 的任务系统的底层对象
 * 
 * 【写入】
 *     new XXTask(target) ->
 *     creep.memory.tasks.push(taskObject.serialize())
 * 
 * 【执行】
 *     taskObjectProto = creep.memory.tasks.top() ->
 *     TaskBase.deserialize(taskObjectProto) ->
 *     if taskObject.run(creep) == OK then:
 *         creep.memory.tasks.pop()
 */
export abstract class TaskBase {

    private proto: SerializedTaskObject;

    serialize() {
        return this.proto;
    }

    constructor(proto: SerializedTaskObject = { name: '', target: {} }) {
        this.proto = proto;
    }

    /**
     * 使用 `creep` 执行任务，返回 `OK` 表示任务完成并删除任务，返回 `ERR_UNFINISHED`
     * 表示下一次还会继续执行该任务
     */
    abstract _run(creep: Creep): OK | ERR_UNFINISHED | ERR_NOT_IN_RANGE;

    /**
     * 检查这个任务是否有意义。这个方法会在 `TaskBase.run` 之前执行，如果返回 `false`，
     * 该任务将直接删除而不执行，并继续处理下一个任务。
     */
    abstract shouldRun(creep: Creep): boolean;

    /**
     * 真正的任务执行方法，抽出重复代码，并支持智能 goTo
     */
    run(creep: Creep) {
        const result = this._run(creep);
        if (result === ERR_NOT_IN_RANGE) {
            creep.goTo(this.target);
        }
        else if (result === ERR_UNFINISHED) {
            if (this.proto.moveCloser) {
                this.moveToTarget(creep, 1);
            }
        }
        return result;
    }

    /**
     * 使 creep 靠近目标
     */
    private moveToTarget(creep: Creep, range = 1) {
        if (creep.pos.getRangeTo(this.targetPos) > range) {
            creep.goTo(this.target, {
                range,
                moveCloser: this.proto.moveCloser
            });
        }
    }

    protected defaultProcess(result: number, okCallback?: () => void) {
        if (result === OK && okCallback) {
            okCallback();
            return ERR_UNFINISHED;
        }
        else if (result === ERR_NOT_IN_RANGE) {
            return result;
        }
        else {
            return OK;
        }
    }

    get name() {
        return this.proto.name;
    }

    get target() {
        if (!this.proto.target.id) {
            return null;
        }
        else {
            return Game.getObjectById(this.proto.target.id);
        }
    }

    get targetPos() {
        if (!this.proto.target.pos) {
            return undefined;
        }
        else {
            const p = this.proto.target.pos;
            if (0 <= p.x && p.x <= 50 && 0 <= p.y && p.y <= 50 && typeof p.roomName === 'string') {
                return new RoomPosition(p.x, p.y, p.roomName);
            }
            else {
                return undefined;
            }
        }
    }

    get taskData() {
        return this.proto.data;
    }
}

declare global {
    interface TaskDataRecorder {
        /** 【可选】资源类型 */
        rType?: ResourceConstant;
        /** 【可选】数量 */
        amount?: number;
        /** 【可选】额外字符串信息 */
        info?: string;
    }
    type CreepTargettableObject =
        | Structure
        | ConstructionSite
        | AnyCreep
        | Source
        | Deposit
        | Mineral
        | Resource;
    interface SerializedTaskObject {
        /** 该任务的名字 */
        name: string;
        /** 任务操作的对象 */
        target: {
            /** 【可选】 对象的 id */
            id?: Id<CreepTargettableObject>;
            /** 【可选】 对象的位置 */
            pos?: {
                x: number;
                y: number;
                roomName: string;
            }
        };
        /** 该任务携带的数据 */
        data?: TaskDataRecorder;
        /** 任务选项：尽可能向目标移动 */
        moveCloser?: boolean;
    }

    interface CreepMemory {
        /** 任务链 */
        tasks?: SerializedTaskObject[];
        /**
         *  这个计时器不为0时，这个爬就啥也不干。每1tick都会减一。
         */
        suspendTimer?: number;
    }

    interface RoomObject {
        targetedBy: Creep[];
    }

    type ERR_UNFINISHED = -13;
    const ERR_UNFINISHED: ERR_UNFINISHED;

    interface Game {
        targetingData: Record<Id<CreepTargettableObject>, Creep[]>;
    }
}

// quick fix
global['ERR_UNFINISHED'] = -13;